package com.wish.nettysimple;

import com.wish.common.MyUtils;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;


/**
 * @author tantexian
 * @since 2016/7/11
 */
public class NettyServer {

    public static void main(String[] args) {
        final int port = args.length >= 1 ? Integer.parseInt(args[0]) : MyUtils.getDefaultPort();
        try {
            new NettyServer().bind(port);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public void bind(int port) throws InterruptedException {
        // 创建NIO线程组（Reactor封装），用于处理网络事件
        // 处理客户端连接NIO线程组
        NioEventLoopGroup accetpNioEventLoopGroup = new NioEventLoopGroup();
        // 处理网络IO事件NIO线程组
        NioEventLoopGroup handleNioEventLoopGroup = new NioEventLoopGroup();

        try {
            // NIO服务启动辅助类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(accetpNioEventLoopGroup, handleNioEventLoopGroup)// 将线程组传递到group中
                .channel(NioServerSocketChannel.class)// 类似java
                                                      // nio中ServerSocketChannel
                .option(ChannelOption.SO_BACKLOG, 1024)// 设置TCP参数
                .childHandler(new myChannelHandler());// 绑定IO事件处理类，类似java
                                                      // nio中的handler，主要用来处理网络IO事件

            // 绑定端口，然后调用同步阻塞方法
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            System.out.println(
                "I,m a smart nettysimple robot and wait for you ask. [I'm listen on port " + port + "]\n");
            // 等待链路关闭之后，才退出main函数
            channelFuture.channel().closeFuture().sync();
        }
        finally {
            // 优雅关闭退出
            accetpNioEventLoopGroup.shutdownGracefully();
            handleNioEventLoopGroup.shutdownGracefully();
        }

    }

    /**
     * IO事件处理类，类似java nio中的handler，主要用来处理网络IO事件
     *
     * @author tantexian
     * @since 2016/7/12
     * @params
     */
    class myChannelHandler extends ChannelInitializer {
        protected void initChannel(Channel channel) throws Exception {
            channel.pipeline().addLast(new RobotServerHandle());
        }
    }

    /**
     * 用于对网络事件进行读写操作
     *
     * @author tantexian
     * @since 2016/7/12
     * @params
     */
    class RobotServerHandle extends ChannelHandlerAdapter {

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf byteBuf = (ByteBuf) msg; // 将msg转换为netty的ByteBuf对象
            byte[] bytes = new byte[byteBuf.readableBytes()]; // 获取缓存区中可读字节数，创建字节数组
            byteBuf.readBytes(bytes); // 通过readBytes将缓冲区byteBuf的数据复制到bytes数组中
            String body = new String(bytes, "UTF-8");
            // System.out.println("I'm robot server, I has receive " + body);
            // 通过获取到client发过来的信息body，获取对应的answer
            String answer = MyUtils.getAnswer(body);
            ByteBuf answerBuf = Unpooled.copiedBuffer(answer.getBytes());
            // 异步发送消息给客户端
            // 为了性能考虑netty中write并不会立即将消息发送给对方，只是放置到发送缓冲区数组中，
            // 只有调用ctx.flush()才将缓冲区中消息写入到SocketChannel中发送给对方
            ctx.writeAndFlush(answerBuf);
        }


        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            // 将消息队列中的消息写入到SocketChannel中发送给对方
            ctx.flush();
        }


        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            ctx.close();
        }
    }
}
